/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.ArrayList;

class DateFormatToFte {
    public static final int TIME_STAMP_SECONDS_CONST = 1167585071;
    public static final long TIME_STAMP_MILLISECONDS_CONST = 1167585071000L;
    public static final int TIME_ADD_CONST = 84266956;
    public static final int TIME_ZONE_OFFSET_CONST = -480;
    public static final int TIME_CONVERT_CONST = 60;
    public static final int TIME_TOTAL_CONST = 3600;
    public static final double TIME_BETA_CONST = 86.4;
    public static final int TIME_HOUR_12_CONST = 12;
    public static final int TIME_HOUR_24_CONST = 24;
    public static final int YEAR_4_CONST = 4;
    public static final int YEAR_STARD_CONST = 100;
    public static final int YEAR_LEEP_CONST = 400;
    public static final int YEAR_TOTAL_DAY_CONST = 364;
    public static final int YEAR_1900_CONST = 1900;
    public static final int YEAR_2000_CONST = 2000;
    public static final int YEAR_2001_CONST = 2001;
    public static final int YEAR_CONST = 2007;
    public static final int NUM_1_CONST = 1;
    public static final int NUM_2_CONST = 2;
    public static final int NUM_3_CONST = 3;
    public static final int NUM_6_CONST = 6;
    public static final int NUM_7_CONST = 7;
    public static final int NUM_9_CONST = 9;
    public static final int NUM_11_CONST = 11;
    public static final int NUM_28_CONST = 28;
    public static final int NUM_29_CONST = 29;
    public static final int NUM_30_CONST = 30;
    public static final int NUM_31_CONST = 31;
    public static final int NUM_38_CONST = 38;
    public static final int NUM_500_CONST = 500;
    public static final int NUM_TIME_CONST = 1000;
    public static final int NUM_LOOPCOUNT_CONST = 80;

    static class MyDate {
        public int timeIntervalSince1970() {
            return TIME_STAMP_SECONDS_CONST;
        }

        public int timeStamp() {
            return TIME_STAMP_SECONDS_CONST;
        }

        public long milliStamp() {
            return TIME_STAMP_MILLISECONDS_CONST;
        }

        MyDate(String str) {}

        public int getDate() {
            //Returns a day of the month
            return NUM_1_CONST;
        }

        public int getTimezoneOffset() {
            //Returns the time difference between Greenwich Mean Time and local time:
            return TIME_ZONE_OFFSET_CONST;
        }

        public int getYear() {
            return YEAR_CONST;
        }

        public int getFullYear() {
            //return a year
            return YEAR_CONST;
        }

        public int getMonth() {
            //return a month
            return NUM_1_CONST;
        }

        public int getHours() {
            //Returns the hour field of the time based on the specified time:
            return NUM_1_CONST;
        }

        public int getMinutes() {
            //Returns the minute field of the time based on the specified time:
            return NUM_11_CONST;
        }

        public int getSeconds() {
            //Returns the second field of the time based on the specified time:
            return NUM_11_CONST;
        }

        public int getDay() {
            //Returns the number of a day of the week.
            return NUM_1_CONST;
        }

        public long getTime() {
            //Returns the number of milliseconds between January 1, 1970:
            int timeInterval = timeIntervalSince1970();
            return (long) timeInterval * NUM_TIME_CONST;
        }

        public void setTime(long millisedcond) {
            //Method to set the Date object in milliseconds.
        }
    }

    public static class DateFormatter {
        public String dateFormat;
        public TimeZone timeZone;

        DateFormatter() {
            this.dateFormat = null;
            this.timeZone = null;
        }

        public MyDate newDate(String from)  {
            return new MyDate(from);
        }
    }

    public static class TimeZone {
        String identifier;

        TimeZone(String identifier) {
            identifier = identifier;
        }
    }

    public boolean arrayExists(String[] array, String x) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] == x) {
                return true;
            }
        }
        return false;
    }

    public static class DateClass extends MyDate {
        public String[] switches = {"a", "A", "B", "d", "D", "F", "g", "G", "h", "H", "i", "j", "l", "L", "m", "M", "n", "O", "r", "s", "S", "t", "U", "w", "W", "y", "Y", "z"};
        public String[] daysLong = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
        public String[] daysShort = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        public String[] monthsShort = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        public String[] monthsLong = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
        public String[] daysSuffix = {
                "st", "nd", "rd", "th", "th", "th", "th", // 1st - 7th
                "th", "th", "th", "th", "th", "th", "th", // 8th - 14th
                "th", "th", "th", "th", "th", "th", "st", // 15th - 21st
                "nd", "rd", "th", "th", "th", "th", "th", // 22nd - 28th
                "th", "th", "st"
        }; // 29th - 31st

        DateClass(String str) {
            super(str);
        }

        public String formatDate(String input, Integer time) {
            // formatDate :
            // a PHP date like function, for formatting date strings
            // See: http://www.php.net/date
            //
            // input : format string
            // time : epoch time (seconds, and optional)
            //
            // if time is not passed, formatting is based on
            // the current "this" date object's set time.
            //
            // supported:
            // a, A, B, d, D, F, g, G, h, H, i, j, l (lowercase L), L,
            // m, M, n, O, r, s, S, t, U, w, W, y, Y, z
            //
            // unsupported:
            // I (capital i), T, Z
            long prevTime = 0;
            if (time != null) {
                // save time
                prevTime = this.getTime();
                this.setTime((long) time);
            }
            ArrayList<String> arr = this.stringToArray(input);
            for (int index = 0; index < arr.size(); index++) {
                if (this.arrayExists(this.switches, arr.get(index))) {
                    String value = arr.get(index);
                    switch (arr.get(index)) {
                        case "a":
                            value = this.a();
                        case "A":
                            value = this.bigA();
                            break;
                        case "B":
                            value = this.bigB();
                            break;
                        case "d":
                            value = this.d();
                            //System.out.println("day is: " + value);
                            break;
                        case "D":
                            value = this.bigD();
                            break;
                        case "F":
                            value = this.bigF();
                            break;
                        case "g":
                            value = this.g();
                            break;
                        case "G":
                            value = String.format("%d", this.bigG());
                            break;
                        case "h":
                            value = this.h();
                            break;
                        case "H":
                            value = this.bigH();
                            break;
                        case "i":
                            value = this.i();
                            break;
                        case "j":
                            value = String.format("%d", this.j());
                            break;
                        case "l":
                            value = this.l();
                            break;
                        case "L":
                            value = String.format("%d", this.bigL());
                            break;
                        case "m":
                            value = this.m();
//                            System.out.println("mouth is : " + value);
                            break;
                        case "M":
                            value = this.bigM();
                            break;
                        case "n":
                            value = String.format("%d", this.n());
                            break;
                        case "O":
                            value = this.bigO();
                            break;
                        case "r":
                            value = this.r();
                            break;
                        case "s":
                            value = this.s();
                            break;
                        case "S":
                            value = this.bigS();
                            break;
                        case "t":
                            value = String.format("%d", this.t());
                            break;
                        case "U":
                            value = String.format("%f", this.bigU());
                            break;
                        case "w":
                            value = String.format("%d", this.w());
                            break;
                        case "W":
                            value = this.bigW();
                            break;
                        case "y":
                            value = this.y();
                            break;
                        case "Y":
                            value = String.format("%d", this.bigY());
//                            System.out.println("year is : " + value);
                            break;
                        case "z":
                            value = String.format("%f", this.z());
                            break;
                        default:
                            break;
                    }
                    arr.set(index, value);
                }
            }
            //reset time, back to what it was
            if (prevTime != 0) {
                this.setTime(prevTime);
            }
            return String.join("", arr);
        }

        public String a() {
            // Lowercase Ante meridiem and Post meridiem
            return this.getHours() > NUM_11_CONST ? "pm" : "am";
        }

        public String bigA() {
            // Uppercase Ante meridiem and Post meridiem
            return this.getHours() > NUM_11_CONST ? "PM" : "AM";
        }

        public String bigB() {
            // Swatch internet time. code simply grabbed from ppk,
            // since I was feeling lazy:
            // http://www.xs4all.nl/~ppk/js/beat.html
            int off = (this.getTimezoneOffset() + TIME_CONVERT_CONST) * TIME_CONVERT_CONST;
            int theSeconds = this.getHours() * TIME_TOTAL_CONST + this.getMinutes() * TIME_CONVERT_CONST + this.getSeconds() + off;
            double beat = Math.floor(theSeconds / TIME_BETA_CONST);
            if (beat > NUM_TIME_CONST) {
                beat -= NUM_TIME_CONST;
            }
            if (beat < 0) {
                beat += NUM_TIME_CONST;
            }
            String beatStr = String.format("%f", beat);
            if (beatStr.length() == 1) {
                beatStr = String.format("00%f", beat);
            }
            if (beatStr.length() == NUM_2_CONST) {
                beatStr = String.format("0%f", beat);
            }
            return beatStr;
        }

        public String d() {
            // Day of the month, 2 digits with leading zeros
            return String.valueOf(this.getDate()).length() == 1 ? String.format("0%d", this.getDate()) : String.format("%d", this.getDate());
        }

        public String bigD() {
            // A textual representation of a day, three letters
            return this.daysShort[this.getDay()];
        }

        public String bigF() {
            // A full textual representation of a month
            return this.monthsLong[this.getMonth() - 1];
        }

        public String g() {
            // 12-hour format of an hour without leading zeros
            return this.getHours() > TIME_HOUR_12_CONST ? String.format("%d", this.getHours() - TIME_HOUR_12_CONST) : String.format("%d", this.getHours());
        }

        public int bigG() {
            // 24-hour format of an hour without leading zeros
            return this.getHours();
        }

        public String h() {
            // 12-hour format of an hour with leading zeros
            if (this.getHours() > TIME_HOUR_12_CONST) {
                String s = String.format("%d", this.getHours() - TIME_HOUR_12_CONST);
                return s.length() == 1 ? "0" + (this.getHours() - TIME_HOUR_12_CONST) : String.valueOf(this.getHours() - TIME_HOUR_12_CONST);
            } else {
                String s = String.format("%d", this.getHours());
                return s.length() == 1 ? "0" + this.getHours() : String.valueOf(this.getHours());
            }
        }

        public String bigH() {
            // 24-hour format of an hour with leading zeros
            return String.format("%d", this.getHours()).length() == 1 ? String.format("0%d", this.getHours()) : String.format("%d", this.getHours());
        }

        public String i() {
            // Minutes with leading zeros
            return String.format("%d", this.getMinutes()).length() == 1 ? String.format("0%d", this.getMinutes()) : String.format("%d", this.getMinutes());
        }

        public int j() {
            // Day of the month without leading zeros
            return this.getDate();
        }

        public String l() {
            // A full textual representation of the day of the week
            return this.daysLong[this.getDay() - 1];
        }

        public int bigL() {
            // leap year or not. 1 if leap year, 0 if not.
            // the logic should match iso's 8601 standard.
            int y_ = this.bigY();
            if ((y_ % YEAR_4_CONST == 0 && y_ % YEAR_STARD_CONST != 0) || (y_ % YEAR_4_CONST == 0 && y_ % YEAR_STARD_CONST == 0 && y_ % YEAR_LEEP_CONST == 0)) {
                return 1;
            } else {
                return 0;
            }
        }

        public String m() {
            // Numeric representation of a month, with leading zeros
            return this.getMonth() < NUM_9_CONST ? String.format("0%d", this.getMonth() + 1) : String.format("%d", this.getMonth() + 1);
        }

        public String bigM() {
            // A short textual representation of a month, three letters
            return this.monthsShort[this.getMonth()];
        }

        public int n() {
            // Numeric representation of a month, without leading zeros
            return this.getMonth() + 1;
        }

        public String bigO() {
            // Difference to Greenwich time (GMT) in hours
            int os = Math.abs(this.getTimezoneOffset());
            String h = String.format("%f", Math.floor((double) os / TIME_CONVERT_CONST));
            String m = String.format("%d", os % TIME_CONVERT_CONST);
            h = h.length() == 1 ? String.format("0%s", h) : "1";
            m = m.length() == 1 ? String.format("0%s", m) : "1";
            return this.getTimezoneOffset() < 0 ? String.format("+%s%s", h, m) : String.format("-%s%s", h, m);
        }

        public String r() {
            // RFC 822 formatted date
            String r;
            r = String.format("%s,%s %s %s %s:%s:%s %s", this.bigD(), this.j(), this.bigM(), this.bigY(), this.bigH(), this.i(), this.s(), this.bigO());
            return r;
        }

        public String bigS() {
            // English ordinal suffix for the day of the month, 2 characters
            return this.daysSuffix[this.getDate() - 1];
        }

        public String s() {
            // Seconds, with leading zeros
            return String.format("%d", this.getSeconds()).length() == 1 ? String.format("0%d", this.getSeconds()) : String.format("%d", this.getSeconds());
        }

        public int t() {
            // thanks to Matt Bannon for some much needed code-fixes here!
            Object[] daysinmonths = {
                    null,
                    NUM_31_CONST,
                    NUM_28_CONST,
                    NUM_31_CONST,
                    NUM_30_CONST,
                    NUM_31_CONST,
                    NUM_30_CONST,
                    NUM_31_CONST,
                    NUM_31_CONST,
                    NUM_30_CONST,
                    NUM_31_CONST,
                    NUM_30_CONST,
                    NUM_31_CONST
			};
            if (this.bigL() == 1 && this.n() == NUM_2_CONST) {
                return NUM_29_CONST; // leap day
            }
            return (int) daysinmonths[this.n()];
        }

        public double bigU() {
            // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT)
            return Math.round((float) this.getTime() / NUM_TIME_CONST);
        }

        public String bigW() {
            // Weeknumber, as per ISO specification:
            // http://www.cl.cam.ac.uk/~mgk25/iso-time.html

            // if the day is three days before newyears eve,
            // there's a chance it's "week 1" of next year.
            // here we check for that.
            double beforeNY = YEAR_TOTAL_DAY_CONST + this.bigL() - this.z();
            double afterNY = this.z();
            int weekday = this.w() != 0 ? this.w() - 1 : NUM_6_CONST; // makes sunday (0), into 6.
            if (beforeNY <= NUM_2_CONST && weekday <= NUM_2_CONST - beforeNY) {
                return "1";
            }
            // similarly, if the day is within threedays of newyears
            // there's a chance it belongs in the old year.
            MyDate ny = this.stringToDate(String.format("1/1/%d 00:00:00", this.bigY()));
            int nyDay = ny.getDay() != 0 ? ny.getDay() - 1 : NUM_6_CONST;
            if (afterNY <= NUM_2_CONST && nyDay >= YEAR_4_CONST && afterNY >= NUM_6_CONST - nyDay) {
                // Since I'm not sure we can just always return 53,
                // i call the function here again, using the last day
                // of the previous year, as the date, and then just
                // return that week.
                DateClass prevNY = new DateClass("December 31 " + String.format("%d", this.bigY() - 1) + " 00:00:00");
                return prevNY.formatDate("W", null);
            }
            // week 1, is the week that has the first thursday in it.
            // note that this value is not zero index.
            if (nyDay <= NUM_3_CONST) {
                // first day of the year fell on a thursday, or earlier.
                return String.format("%f", 1 + Math.floor((this.z() + nyDay) / NUM_7_CONST));
            } else {
                // first day of the year fell on a friday, or later.
                return String.format("%f", 1 + Math.floor((this.z() - (NUM_7_CONST - nyDay)) / NUM_7_CONST));
            }
        }

        public int w() {
            // Numeric representation of the day of the week
            return this.getDay();
        }

        public int bigY() {
            // A full numeric representation of a year, 4 digits
            // we first check, if getFullYear is supported. if it
            // is, we just use that. ppks code is nice, but wont
            // work with dates outside 1900-2038, or something like that
            if (this.getFullYear() != 0) {
                DateClass newDate = new DateClass(String.format("January 1 %d 00:00:00 +0000", YEAR_2001_CONST));
                int x = newDate.getFullYear();
                if (x == YEAR_2001_CONST) {
                    // i trust the method now
                    return this.getFullYear();
                }
            }
            // else, do this:
            // codes thanks to ppk:
            // http://www.xs4all.nl/~ppk/js/introdate.html
            int x = this.getFullYear();
            int y = x % YEAR_STARD_CONST;
            y += y < NUM_38_CONST ? YEAR_2000_CONST : YEAR_1900_CONST;
            return y;
        }

        public String y() {
            // A two-digit representation of a year
            String y = String.format("%d", this.bigY());
            return y.substring(y.length() - NUM_2_CONST, y.length());
        }

        public double z() {
            // The day of the year, zero indexed! 0 through 366
            DateClass t = new DateClass("January 1 " + this.bigY() + " 00:00:00");
            long diff = this.getTime() - t.getTime();
            return Math.floor((double) diff / NUM_TIME_CONST / TIME_CONVERT_CONST / TIME_CONVERT_CONST / TIME_HOUR_24_CONST);
        }

        public ArrayList<String> stringToArray(String str) {
            final ArrayList<String> arr = new ArrayList<>();
            for (int i = 0; i < str.length(); i++) {
                arr.add(String.valueOf(str.charAt(i)));
            }
            return arr;
        }

        public MyDate stringToDate(String str) {
            DateFormatter dateF = new DateFormatter();
            dateF.dateFormat = "dd-MM-yyyy HH:mm:ss";
            dateF.timeZone = new TimeZone("Asia/Shanghai");
            MyDate dateObject = dateF.newDate(str);
            return dateObject;
        }

        public boolean arrayExists(String[] array, String x) {
            for (int i = 0; i < array.length; i ++) {
                if (array[i].equals(x)) {
                    return true;
                }
            }
            return false;
        }
    }

    public static class ArkTools {
        public static long timeInUs(){return System.nanoTime();};
    }
}
/*
 * @State
 * @Tags Jetstream2
 */
class Benchmark {
    /*
     * @Benchmark
     */
    public static void run() {
        DateFormatToFte.DateClass date = new DateFormatToFte.DateClass("1/1/2007 01:11:11");
        for (int i = 0; i < DateFormatToFte.NUM_500_CONST; ++i) {
            String shortFormat = date.formatDate("Y-m-d", null);
            String longFormat = date.formatDate("l, F d, Y g:i:s A", null);
            date.setTime(date.getTime() + DateFormatToFte.TIME_ADD_CONST);
//            System.out.println("Y-m-d: " +  shortFormat);
//            System.out.println("l, F d, Y g:i:s A: " +  longFormat);
        }
    }
    public static void runIterationTime() {
        long start = DateFormatToFte.ArkTools.timeInUs();
        for (int i = 0; i < DateFormatToFte.NUM_LOOPCOUNT_CONST; i++) {
            run();
        }
        long end = DateFormatToFte.ArkTools.timeInUs();
        double duration = (end - start) / 1_000_000.0;
        System.out.println("date-format-tofte: ms = " + duration);
    }
    public static void main(String[] args) {
        runIterationTime();;
    }
}









